﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Text;

namespace Toolkit.Csv.Test
{
    static class HelperExtensions
    {
        public static bool SameArrays(this IEnumerable<IEnumerable<string>> first, IEnumerable<IEnumerable<string>> second)
        {
            if (!first.Any() && !second.Any())
                return true;
            else if (first.Count() == second.Count())
                return first.SequenceEqual(second, new RecordComparator());
            return false;
        }

        public static bool SameDicts(this IEnumerable<IDictionary<string, string>> first, IEnumerable<IDictionary<string, string>> second)
        {
            if (!first.Any() && !second.Any())
                return true;
            else if (first.Count() == second.Count())
                return first.SequenceEqual(second, new DictionaryComparator());
            return false;
        }

        public static bool SameDynamics(this IEnumerable<ExpandoObject> first, IEnumerable<ExpandoObject> second)
        {
            if (!first.Any() && !second.Any())
                return true;
            else if (first.Count() == second.Count())
                return first.SequenceEqual(second, new DynamicComparator());
            return false;
        }

        public static string ListToCsv(this IEnumerable<IEnumerable<string>> records)
        {
            string result = "";

            if (!records.Any())
                return result;

            records.ToList().ForEach(record => result += string.Join(",", record) + "\n");
            return result;
        }

        public static string DictsToString(this IEnumerable<IDictionary<string,string>> records)
        {
            string result = "";
            records.ToList().ForEach(record => result += string.Join(", ", record.Keys.Select(k =>
                {
                    string value;
                    record.TryGetValue(k, out value);
                    return k + "=" + value;
                })) + "\n");
            return result;
        }

        public static string DynamicsToString(this IEnumerable<ExpandoObject> records)
        {
            string result = "";
            records.ToList().ForEach(record => result += DynToString(record) + "\n");
            return result;
        }

        public static string DynToString(ExpandoObject expando)
        {
            var record = expando as IDictionary<string, object>;
            return string.Join(", ", record.Keys.Select(k =>
                {
                    object value;
                    record.TryGetValue(k, out value);
                    return k + "=" + value;
                }));
        }

        public static IEnumerable<Dictionary<string, string>> ListToDicts(this IEnumerable<IEnumerable<string>> records)
        {
            var result = new List<Dictionary<string, string>>();

            if (!records.Any())
                return result;

            var fields = records.First().ToArray();
            records.Skip(1).ToList().ForEach(record =>
                {
                    var dict = new Dictionary<string, string>();
                    foreach (var idx in Enumerable.Range(0, fields.Length))
                        dict[fields[idx]] = record.ElementAt(idx);
                    result.Add(dict);
                });
            return result;
        }


        public static IEnumerable<ExpandoObject> ListToDynamics(this IEnumerable<IEnumerable<string>> records)
        {
            var result = new List<ExpandoObject>();

            if (!records.Any())
                return result;

            var fields = records.First().ToArray();
            records.Skip(1).ToList().ForEach(record =>
                {
                    var expo = new ExpandoObject();
                    var dict = expo as IDictionary<string, object>;
                    foreach (var idx in Enumerable.Range(0, fields.Length))
                    {
                        if (idx < record.Count() && idx < records.Count())
                            dict[fields[idx]] = record.ElementAt(idx);
                    }
                result.Add(expo);
            });
            return result;
        }

        public static bool IsRectangular<T>(this IEnumerable<IEnumerable<T>> data)
        {
            if (!data.Any())
                return true;
            var numRows = data.ElementAt(0).Count();
            return data.ToList().All(row => row.Count() == numRows);
        }
    }

    public class RecordComparator : IEqualityComparer<IEnumerable<object>>
    {
        public bool Equals(IEnumerable<object> x, IEnumerable<object> y)
        {
            return x.SequenceEqual(y);
        }

        public int GetHashCode(IEnumerable<object> obj)
        {
            return obj.GetHashCode();
        }
    }

    public class DictionaryComparator : IEqualityComparer<IDictionary<string, string>>
    {
        public bool Equals(IDictionary<string, string> x, IDictionary<string, string> y)
        {
            foreach (var kvp in x)
            {
                string yValue;
                if (!y.TryGetValue(kvp.Key, out yValue)) return false;
                if (!string.Equals(kvp.Value, yValue)) return false;
            }
            return true;
        }

        public int GetHashCode(IDictionary<string, string> obj)
        {
            return obj.GetHashCode();
        }
    }

    public class DynamicComparator : IEqualityComparer<ExpandoObject>
    {
        public bool Equals(ExpandoObject x, ExpandoObject y)
        {
            var xdict = x as IDictionary<string, object>;
            var ydict = y as IDictionary<string, object>;
            foreach (var kvp in xdict)
            {
                object yValue;
                if (!ydict.TryGetValue(kvp.Key, out yValue)) return false;
                if (!string.Equals(kvp.Value, yValue)) return false;
            }
            return true;
        }

        public int GetHashCode(ExpandoObject obj)
        {
            return obj.GetHashCode();
        }
    }
}
